/**
 * @file LightBlob.cpp
 * @author enemy1205 (enemy1205@qq.com)
 * @brief 灯条构造类
 * @date 2021-08-16
 */
#include "LightBlob.h"

using namespace cv;
using namespace std;
//#define LIGHTDEBUG

/**
 * @brief 寻找有效灯条
 */
void Light_blob::isValidLightblob(vector<Point> &contours, RotatedRect &rect) {
    double areaRatio = contourArea(contours) / rect.size.area();//实际面积/拟合面积
//    cout << "面积比" << areaRatio << endl;
    height = max(rect.size.height, rect.size.width);/*长边*/
    width = min(rect.size.height, rect.size.width);/*短边*/
//    cout << height << " " << width << endl;
    angle = rect.angle;
    //获取修正后的灯条角度
    if (angle > 90) angle -= 180;
    //过滤掉一些小光斑
    if (rect.size.area() < L_param.min_area)return;
    if (height < L_param.light_min_length)return;
        //判断为远处装甲板
    else if (height < L_param.far_max_length) {
        if (hw_ratio() < L_param.far_min_ratio or hw_ratio() > L_param.far_max_ratio) {
#ifdef LIGHTDEBUG
            cout << "远处矩形高宽比不符合要求" << "  hw_ratio:" << hw_ratio() << endl;
#endif
            return;
        }/*其余为近处灯条*/
    } else {
        if (hw_ratio() > L_param.light_max_ratio) {
#ifdef LIGHTDEBUG
            cout << "矩形高宽比不符合要求" << "  hw_ratio:" << hw_ratio() << endl;
#endif
            return;
        }
    }
    if (abs(this->angle) > L_param.light_max_angle) //过滤掉接近水平的血条
    {
#ifdef LIGHTDEBUG
        cout << "矩形角度过于水平" << "  angle:" << abs(this->angle) << endl;
#endif
        return;
    }
    if (areaRatio < L_param.light_min_area_ratio) {
#ifdef LIGHTDEBUG
        cout << "矩形面积比过小" << "  areaRatio:" << areaRatio << endl;
#endif
        return;
    }
    this->match_error = abs(angle) * L_param.ERR_ANGLE + areaRatio * L_param.ERR_AREA_RATIO;
    validity = true;
}

/**
 * @brief 获取更加准确的关键点信息
 */
void Light_blob::getTruePoint(vector<Point> &contours) {
    Point bottom = Point(-1, -1);
    Point top = Point(-1, -1);
    float min_distance_top = MAXFLOAT;
    float min_distance_botoom = MAXFLOAT;
    // 遍历每一个轮廓点
    for (const auto &point: contours) {
        // 高于或低于一定高度时才对其进行计算比较，节省运算
        if (point.y < 2.f / 3 * this->center.y) {
            // 找出离原来上顶点最近的点作新上顶点
            float tmp_distance = getDistance(point, this->top_point);
            if (tmp_distance < min_distance_top) {
                min_distance_top = tmp_distance;
                top = point;
            }
        }
        if (point.y > 4.f / 3 * this->center.y) {
            // 找出离原来下顶点最近的点作新下顶点
            float tmp_distance = getDistance(point, this->bottom_point);
            if (tmp_distance < min_distance_botoom) {
                min_distance_botoom = tmp_distance;
                bottom = point;
            }
        }
    }
    // 提取轮廓点集中离拟合椭圆上下顶点最近的点作修正后的数据
    if (top != Point(-1, -1) &&
        bottom != Point(-1, -1)) {
        // 更新上下顶点
        this->top_point = top;
        this->bottom_point = bottom;
        // 更新灯条中点
        this->center = (this->top_point + this->bottom_point) / 2.f;
        float temp_height = getDistance(this->top_point, this->bottom_point);
        this->width = this->width * temp_height / this->height;
        this->height = temp_height;
    }
}

/**
 * @brief 灯条构造函数
 * @param contours 点集轮廓
 * @param rect 拟合外接旋转矩形
 */
Light_blob::Light_blob(vector<Point> &contours, RotatedRect &rect) {
//    this->contours = move(contours); /*减少拷贝次数上,move>exchange>swap*/
//    this->rect = rect;
    this->isValidLightblob(contours, rect);
    if (this->validity) {
        rect.points(vertexes);
        center = rect.center;
//        area = rect.size.area();//面积暂未使用
        sort(begin(this->vertexes), end(this->vertexes),
             [&](Point2f &pt_1, Point2f &pt_2) -> bool {
                 return pt_1.y < pt_2.y;
             });
        // 获得上顶点
        this->top_point = (this->vertexes[0] + this->vertexes[1]) / 2;
        // 获得下顶点
        this->bottom_point = (this->vertexes[2] + this->vertexes[3]) / 2;
        this->getTruePoint(contours);
        this->rect = make_shared<RotatedRect>(center, rect.size, angle);
    }
}

